<script>
  import {onMount, tick} from 'svelte';
  import {_} from '../locales';
  import Section from './Section.svelte';
  import Button from './Button.svelte';
  import DropArea from './DropArea.svelte';
  import ComplexMessage from './ComplexMessage.svelte';
  import ImportingProject from './ImportProject.svelte';
  import writablePersistentStore from './persistent-store';
  import {progress, currentTask} from './stores';
  import {UserError} from '../common/errors';
  import getProjectMetadata from './get-project-metadata';
  import loadProject from '../packager/load-project';
  import {extractProjectId, isValidURL, getTitleFromURL} from './url-utils';
  import Task from './task';
  import importExternalProject from './import-external-project';

  const defaultProjectId = '60917032';

  const type = writablePersistentStore('SelectProject.type', 'id');
  const projectId = writablePersistentStore('SelectProject.id', defaultProjectId);
  const projectUrl = writablePersistentStore('SelectProject.url', '');

  const projectIdInURL = /^#\d+$/.test(location.hash) ? location.hash.substring(1) : null;
  if (projectIdInURL) {
    $type = 'id';
    $projectId = projectIdInURL;
  }

  let isImportingProject = false;
  importExternalProject({
    onStartImporting: () => {
      isImportingProject = true;
    },
    onCancelImporting: () => {
      isImportingProject = false;
    },
    onFinishImporting: (files) => {
      if (!isImportingProject) {
        // Import was cancelled.
        return;
      }
      $type = 'file';
      isImportingProject = false;
      fileInputElement.files = files;
      setFiles(files);
    }
  });

  export let projectData = null;
  const resetProjectAndCancelTask = () => {
    projectData = null;
    currentTask.abort();
  };

  // Reset project when input changes
  $: $projectId, $type, resetProjectAndCancelTask();

  // just incase some non-number string was stored from older versions
  $projectId = extractProjectId($projectId);

  const getDisplayedProjectURL = () => `https://scratch.mit.edu/projects/${$projectId}`;

  const submitOnEnter = (e) => {
    if (e.key === 'Enter') {
      load();
    }
  };
  const handleInput = (e) => {
    $projectId = extractProjectId(e.target.value);
    e.target.value = getDisplayedProjectURL();
  };
  const handleFocus = (e) => {
    e.target.select();
  };

  let fileInputElement;

  const copyFileList = (files) => {
    const transfer = new DataTransfer();
    for (const file of files) {
      transfer.items.add(file);
    }
    return transfer.files;
  };

  // This is used to remember files between reloads in some browsers (currently only Firefox)
  // This element is defined in the HTML of template.ejs because some browsers won't
  // autocomplete file inputs generated by JS.
  const inputForRememberingProjectFile = document.querySelector('.input-for-remembering-project-file');
  if (inputForRememberingProjectFile) {
    // Check for autocompleted files after mount so that fileInputElement is defined.
    onMount(() => {
      const storedFiles = inputForRememberingProjectFile.files;
      if (storedFiles.length) {
        fileInputElement.files = copyFileList(storedFiles);
      }
    });
  }

  const setFiles = (files) => {
    resetProjectAndCancelTask();
    if (fileInputElement.files !== files) {
      fileInputElement.files = files;
    }
    if (inputForRememberingProjectFile) {
      inputForRememberingProjectFile.files = copyFileList(files);
    }
    if (files.length && $type === 'file') {
      // if $type was updated before calling this function, wait for the current task to get
      // cancelled before we start the next one
      tick().then(load);
    }
  };
  const handleDrop = ({detail: dataTransfer}) => {
    const name = dataTransfer.files[0].name;
    if (name.endsWith('.sb') || name.endsWith('.sb2') || name.endsWith('.sb3')) {
      $type = 'file';
      setFiles(dataTransfer.files);
    }
  };
  const handleFileInputChange = (e) => {
    setFiles(e.target.files);
  };

  const internalLoad = async (task) => {
    let uniqueId = '';
    let id = null;
    let projectTitle = '';
    let project;

    const progressCallback = (type, a, b) => {
      if (type === 'fetch') {
        task.setProgress(a);
      } else if (type === 'assets') {
        task.setProgressText(
          $_('progress.loadingAssets')
            .replace('{complete}', a)
            .replace('{total}', b)
        );
        task.setProgress(a / b);
      } else if (type === 'compress') {
        task.setProgressText($_('progress.compressingProject'));
        task.setProgress(a);
      }
    };

    if ($type === 'id') {
      id = $projectId;
      if (!id) {
        throw new UserError($_('select.invalidId'));
      }
      uniqueId = `#${id}`;

      task.setProgressText($_('progress.loadingProjectMetadata'));
      const metadata = await getProjectMetadata(id);

      const token = metadata.token;
      projectTitle = metadata.title;

      task.setProgressText($_('progress.loadingProjectData'));
      const {promise: loadProjectPromise, terminate} = await loadProject.fromID(id, token, progressCallback);
      task.whenAbort(terminate);
      project = await loadProjectPromise;
    } else if ($type === 'file') {
      const files = fileInputElement.files;
      const file = files && files[0];
      if (!file) {
        throw new UserError($_('select.noFileSelected'));
      }
      uniqueId = `@${file.name}`;
      projectTitle = file.name;
      task.setProgressText($_('progress.compressingProject'));
      project = await (await loadProject.fromFile(file, progressCallback)).promise;
    } else if ($type === 'url') {
      const url = $projectUrl;
      if (!isValidURL(url)) {
        throw new UserError($_('select.invalidUrl'));
      }
      uniqueId = `$${url}`;
      projectTitle = getTitleFromURL(url);
      task.setProgressText($_('progress.loadingProjectData'));
      project = await (await loadProject.fromURL(url, progressCallback)).promise;
    } else {
      throw new Error('Unknown type');
    }

    return {
      projectId: id,
      uniqueId,
      title: projectTitle,
      project,
    };
  };
  const load = async () => {
    resetProjectAndCancelTask();
    const task = new Task();
    projectData = await task.do(internalLoad(task));
    task.done();
  };
</script>

<style>
  input[type="text"] {
    max-width: 300px;
    flex-grow: 1;
  }
  .options {
    margin: 12px 0;
  }
  .option {
    min-height: 25px;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
  }
  input[type="text"], input[type="file"] {
    margin-left: 4px;
  }
</style>

{#if isImportingProject}
  <ImportingProject on:cancel={() => {
    isImportingProject = false;
  }} />
{/if}

<DropArea on:drop={handleDrop}>
  <Section accent="#4C97FF">
    <h2>{$_('select.select')}</h2>
    <p>{$_('select.selectHelp')}</p>

    <div class="options">
      <div class="option">
        <label>
          <input type="radio" name="project-type" bind:group={$type} value="id">
          {$_('select.id')}
        </label>
        {#if $type === "id"}
          <input type="text" value={getDisplayedProjectURL()} spellcheck="false" on:keypress={submitOnEnter} on:input={handleInput} on:focus={handleFocus}>
        {/if}
      </div>
      <!-- TurboWarp Desktop looks for the file-input-option class for special handling, so be careful when modifying this. -->
      <div class="option file-input-option">
        <label>
          <input type="radio" name="project-type" bind:group={$type} value="file">
          {$_('select.file')}
        </label>
        <input hidden={$type !== "file"} on:change={handleFileInputChange} bind:this={fileInputElement} type="file" accept=".sb,.sb2,.sb3">
      </div>
      <div class="option">
        <label>
          <input type="radio" name="project-type" bind:group={$type} value="url">
          {$_('select.url')}
        </label>
        {#if $type === "url"}
          <input type="text" bind:value={$projectUrl} spellcheck="false" placeholder="https://..." on:keypress={submitOnEnter}>
        {/if}
      </div>
    </div>

    {#if $type === "id"}
      <p>
        {$_('select.unsharedProjects')}
      </p>
      <p>
        {$_('select.unsharedProjectsWorkaround')}
      </p>
      <p>
        <ComplexMessage
          message={$_('select.unsharedProjectsMore')}
          values={{
            link: {
              text: 'https://docs.turbowarp.org/unshared-projects',
              href: 'https://docs.turbowarp.org/unshared-projects',
              newTab: true
            }
          }}
        />
      </p>
    {/if}

    <Button on:click={load} text={$_('select.loadProject')} />
  </Section>
</DropArea>

{#if !$progress.visible && !projectData}
  <Section caption>
    <p>{$_('select.loadToContinue')}</p>
  </Section>
{/if}
